package keyword

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strconv"
)

/*
定义关键字结构体
*/
type KeyWord struct {
	searchName     string
	InitArray      []int
	InitMapBool    map[string]bool            //初始化一个key为字符串值为bool类型的map
	InitMapArry    map[string][]string        //初始化一个key为字符串值为数组，数组的值为字符串的map
	InitMapMap     map[string]map[string]bool // 初始化一个key为字符串，数据类型为map，map的值为字符串，值是bool的
	InitMapValue   map[string]bool
	InitPersonInfo *studentInfo `json.go:"studentInfo"`
}

/*
学生信息
*/
type studentInfo struct {
	Name     string   `json:"name"`     // 姓名
	Species  string   `json:"species"`  // 类型
	Age      int      `json:"age"`      // 年龄
	Sex      string   `json:"sex"`      // 性别
	Favorite []string `json:"favorite"` // 爱好
}

var keyWord_ *KeyWord

/*
	for：用于循环。
	range：用于迭代数组、切片、字符串或map。
	break : 终止循环
	goto: 用于跳转到代码中的某个标签位置。
	continue：用于跳过当前循环的剩余代码，直接开始下一次迭代。
*/

func (k *KeyWord) keyword_loop() {
	// 初始化一个长度为3容量为5的数字
	NameList1 := make([]string, 3, 5)
	NameList1[0] = "张三"
	NameList1[1] = "李四"
	NameList1[2] = "王五"
	/*
		将NameList1拷贝一份成NameList2
		NameList1和NameList2共用一个内存地址
		NameList2里面对应的值发生改变,NameList1里面的值也会发生改变
		NameList2[0] = k.searchName + "1"
		NameList2[1] = k.searchName + "2"
		NameList2[2] = k.searchName + "3"
		Operation results:
		searchName: 小明
		NameList1: [小明1 小明2 小明3],长度:3,容量:5
		NameList2: [小明1 小明2 小明3 小明 小明 小明 小明],长度:7,容量:10
	*/
	/*
		切片之后,从索引2位置开始3结束。3取不到
		len() = 1
		cap() = NameList1(5-3) + 切片获得的1 = 3
	*/
	NameList2 := NameList1[2:3]
	NameList2 = append(NameList2, k.searchName+"1")
	NameList2 = append(NameList2, k.searchName+"2")
	NameList2 = append(NameList2, k.searchName+"3")
	NameList2[0] = k.searchName
	/*
		NameList2进行append,一旦append这个容量超过原来设置的容量就会发生扩容。NameList2将会将会分配一个全新的内存空间,容量是之前的两倍
		NameList2和NameList1将会毫无关联。两个成为独立的数组
		Operation results:
		searchName: 小明
		NameList1: [张三 李四 王五],长度:3,容量:5
		NameList2: [小明 李四 王五 小明 小明 小明],长度:6,容量:10
	*/
	/*
		切片扩容的规律
		当容量比较小的时候,会以2倍的对原先容量进行扩容，当容量很大时，避免内存浪费。扩容的倍数就会变得小方
	*/
	arr := make([]int, 3)
	prevCap := cap(arr)
	for i := 0; i < 2000; i++ {
		arr = append(arr, i)
		currentCap := cap(arr)
		if currentCap != prevCap {
			fmt.Println(prevCap, " :扩容前 ------> 扩容后: ", currentCap)
			prevCap = currentCap
		}
	}
	fmt.Println("searchName:", k.searchName)
	fmt.Printf("NameList1: %v,长度:%d,容量:%d\n", NameList1, len(NameList1), cap(NameList1))
	fmt.Printf("NameList2: %v,长度:%d,容量:%d\n", NameList2, len(NameList2), cap(NameList2))
}

/*
map 类型的原理
*/
func (k *KeyWord) keyword_map() {
	/*
		初始化map容量为0,每次添加一个新的key就会进行扩容的操作，扩容倍数类似于切片或者数组
		每次发生扩容,当map数组利用率超过0.75就会发生扩容。扩容之后会对key内存地址进行宠幸分配。导致每次打印出来key都不相同
	*/
	k.InitMapBool[k.searchName] = true
	k.InitMapBool["张1"] = true
	k.InitMapBool["李2"] = true
	k.InitMapBool["王3"] = true
	k.InitMapBool["李4"] = true
	k.loop(k.InitMapBool)
	k.loop(k.InitMapBool)
	for i := 5; i < 15; i++ {
		k.InitMapBool[k.searchName+strconv.Itoa(i)] = false
	}
	k.loop(k.InitMapBool)

	k.loop(k.InitMapValue)
	/*
		定义一个key为字符串,值为数组类型的map
	*/
	k.InitMapArry[k.searchName] = []string{
		"唱", "跳", "rap", "篮球",
	}
	k.InitMapArry["周管祥"] = []string{
		"唱", "跳", "rap", "篮球",
	}
	/*
		定义一个key为map值为map的数据
	*/
	k.InitMapMap[k.searchName] = map[string]bool{
		"二狗": true,
		"张三": true,
		"李四": false,
		"王五": true,
		"马六": true,
	}
	/*
		在结构体中定义了json格式的数据
	*/
	jsonData, err := json.Marshal(k.InitPersonInfo)
	if err != nil {
		fmt.Println("json.go marshallIndent err", err)
	}
	/*
		判断map里面对应key是否存在
	*/
	if value, ok := k.InitMapArry["周管祥1"]; ok {
		fmt.Printf("k.InitMapArry['周管祥']=%v\n", value)
	} else {
		fmt.Printf("k.InitMapArry['周管祥1'] invild key,%v!\n", value)
	}
	fmt.Println("InitArray:", k.InitArray)
	fmt.Println("InitMapArray:", k.InitMapArry)
	fmt.Println("InitMapMap:", k.InitMapMap)
	fmt.Println("keyword.studentInfo:", string(jsonData))
}

/*
迭代切片,数组，映射等
*/
func (k *KeyWord) ranges() {
	// 迭代数组
	for k, v := range k.InitArray {
		fmt.Println(k, "---------->", v)
	}
	fmt.Println("************* 数 组 *************")
	// 迭代切片
	arr := k.InitArray[2:5]
	for k, v := range arr {
		fmt.Println(k, "---------->", v)
	}
	fmt.Println("************* 切 片 *************")
	// 迭代map
	for k, v := range k.InitMapValue {
		fmt.Println(k, "---------->", v)
	}
	fmt.Println("************* m a p *************")
}

/*
	defer 释放资源
	defer 可以用于任何函数调用，但最常用于那些需要确保执行的清理操作。
	defer 会在函数返回之前执行，如果函数中有多个返回路径，defer 会在每个返回之前执行。
	在 defer 中使用变量时要小心，因为变量的值是在 defer 语句执行时确定的，而不是在函数调用时确定的。
	defer 可能会影响性能，因为它涉及到延迟调用的额外开销，所以应该在确实需要确保执行时才使用。
*/

func (k *KeyWord) defers() {
	/*
		一般用于释放资源,比如给文件写入hello world!
	*/
	// 文件不存在则创建
	/*
		os.O_APPEND 确保写入操作追加到文件末尾。
		os.O_CREATE 确保如果文件不存在则创建新文件。
		os.O_WRONLY 表示文件以只写方式打开。
		0666 是文件的权限模式，表示文件可读可写。
	*/
	file, err := os.OpenFile("./config/example.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println("err", err)
	}
	// 确保文件在数据返回前,都会关闭
	defer file.Close()

	jsonData, err := json.MarshalIndent(k.InitPersonInfo, "", "   ")
	if err != nil {
		fmt.Println("json.go marshallIndent err", err)
	}
	_, err = io.WriteString(file, string(jsonData)+"\n")
	if err != nil {
		panic(err)
	}
	/*
		多个defer时候一般是后进先出
		this is a second defer!
		this is a first defer!
	*/
	defer fmt.Println("this is a first defer!")
	defer fmt.Println("this is a second defer!")
	fmt.Println("数据写入成功!")
}

/*
	goto用法
	goto 是一个保留关键字，但它并不常用。goto 可以用来无条件地跳转到代码块内的任意位置，
	但它的使用通常被认为是一种糟糕的编程实践，因为它会导致代码的流程变得难以理解和维护。
	因此，Go语言中的goto通常只在一些特定的上下文中使用，比如跳转到错误处理代码块或者循环。
*/

func (k *KeyWord) gotos() {
	for i := 0; i < 1000; i++ {
		if i == 666 {
			goto Loop1
		} else if i == 888 {
			goto Loop2
		} else if i == 999 {
			goto Loop3
		}
	}
	fmt.Println("This will not be printed!")

Loop1:
	fmt.Println("恭喜您中奖666!")
Loop2:
	fmt.Println("恭喜您中奖888!")
Loop3:
	fmt.Println("恭喜您中奖999!")
}

/*
循环break continue用法
break: 用于终止最近的一个循环或select语句。
continue: 跳过当前循环的剩余代码，直接开始下一次迭代。
*/
func (k *KeyWord) ForLoop() {
	//break 阻断循环
	for i := 0; i < 10; i++ {
		if i == 6 {
			// i == 6 跳出循环: 就不会打印6直接打印7
			fmt.Println("跳出循环------>", i)
			continue
		} else if i == 8 {
			// i == 8 之后: break阻断循环,然后退出循环
			fmt.Println("阻断循环------>", i)
			break
		}
		// i只能打印到:5
		fmt.Println("循环体打印:", i)
	}
}

/*
switch 多分支条件判断
case: 在switch语句中，用于定义条件。
fallthrough: 用于在select或switch语句中，强制执行下一个case。
default: 在switch语句中，如果没有匹配的case，则执行default块。
Switch相比较于if else优点:

	可读性：
	switch语句在处理多个条件时，代码更清晰，可读性更好。它允许你将多个条件组织在一起，而不是嵌套多个if-else语句。
	if-else语句通常用于单一条件的判断，或者当条件比较复杂，不容易用switch表达时。
	执行流程：
	在switch语句中，一旦某个case匹配成功，后续的case会自动跳过（除非使用fallthrough）。
	在if-else语句中，只有当当前的条件不满足时，才会继续检查下一个条件。
	使用场景：
	switch语句适合用于多个离散值的匹配，例如枚举类型、固定常量或者简单的整数。
	if-else语句适合用于范围检查、复杂逻辑判断或者需要执行多个条件检查的情况。
	类型断言：
	switch语句可以用于类型断言，这是if-else无法做到的。例如，你可以使用switch来检查一个接口变量的实际类型。
	值赋值：
	在switch语句中，每个case后面可以跟一个表达式，这个表达式的结果可以被赋值给一个变量。
	if-else语句没有这种赋值机制。
	默认行为：
	switch语句中的default块会在没有匹配的case时执行，类似于if-else语句中的else块。
	性能：
	在某些情况下，switch语句可能比if-else语句稍微快一些，因为编译器可以优化switch语句的跳转表。
	简洁性：
	switch语句通常更简洁，尤其是在处理多个条件时，可以减少代码的嵌套。
*/
func (k *KeyWord) switchs(searchName string) {
	// swich 多条件
	switch searchName {
	case "黑熊":
		fmt.Println("我的阿玛尼!我要你赔我的阿玛尼!")
	case "徐凤年":

		fmt.Println("8个爷爷救孙子,我徐凤年天下无敌!")
	case "任达华":
		fmt.Println("软饭硬吃任达华,我还有另一重身份魔鬼筋肉人!")
	default:
		fmt.Println("我不是说你,我是说在座各位都是垃圾!")
	}
	fmt.Println("-------------------------")
	// 表达式switch用法
	var num1, num2 int
	num1 = 1
	num2 = 2
	switch {
	case num1 == num2:
		fmt.Println(num1, "==", num2, "相等")
	case num1 > num2:
		fmt.Println(num1, ">", num2, "大于")
	default:
		fmt.Println(num1, "!=", num2, "不等于")
	}
	fmt.Println("-------------------------")
	// switch中fallthrough用法,fallthrough执行一个case后,继续执行下一个case
	for i := 0; i < 3; i++ {
		switch i {
		case 1, 2, 3:
			fmt.Println(i, "是 1 or 2 or 3")
			fallthrough
		case 4:
			fmt.Println(i, "是 4")
		case 999:
			fmt.Println(i, "是 999")
		}
	}

}

/*
循环map
*/
func (k *KeyWord) loop(mp map[string]bool) {
	for k := range mp {
		fmt.Println(k)
	}
	fmt.Println("---------------------------")
}

/*
主执行方法
*/
func KeyWordsMain() {
	// 实例化传参
	keyWord_ = &KeyWord{
		searchName: "小明0",
		InitArray: []int{
			1, 2, 4, 5, 6, 7, 8, 8,
		},
		InitMapBool: make(map[string]bool),
		InitMapArry: make(map[string][]string),
		InitMapMap:  make(map[string]map[string]bool),
		InitMapValue: map[string]bool{
			"apple":  true,
			"banana": true,
			"orange": true,
			"pear":   true,
		},
		InitPersonInfo: &studentInfo{
			Name:    "周管祥",
			Species: "ikun",
			Sex:     "男",
			Age:     23,
			Favorite: []string{
				"唱", "跳", "rap", "篮球",
			},
		},
	}
	// 切片扩容原理
	// keyWord_.keyword_loop()
	// map 结构体参数应用
	// keyWord_.keyword_map()
	// range 迭代
	// keyWord_.ranges()
	// defer 一般用于释放资源。
	// keyWord_.defers()
	// goto 用法
	// keyWord_.gotos()
	// 循环中break continue用法
	// keyWord_.ForLoop()
	// switch 用法
	// keyWord_.switchs('任达华')
}
